package com.zh.work.aop;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.zh.common.util.*;
import com.zh.common.entity.JSONResult;
import com.zh.common.entity.Oauth;
import com.alibaba.fastjson.JSONObject;
import com.zh.work.dao.platform.agent.AgentInfoMapper;
import com.zh.work.dao.record.AgentRecordMapper;
import com.zh.work.entity.AgentRecordEntity;
import com.zh.work.entity.agent.AgentInfoEntity;
import com.zh.work.entity.agent.AgentRelateEntity;
import com.zh.work.entity.agent.ChangeAgentAccount;
import com.zh.work.entity.agent.QrCodeAgentFee;
import com.zh.work.util.plateform.AgentContrasUtil;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * code is far away from bug with the animal protecting
 * ┏┓　　　┏┓
 * ┏┛┻━━━┛┻┓
 * ┃　　　　　　　┃
 * ┃　　　━　　　┃
 * ┃　┳┛　┗┳　┃
 * ┃　　　　　　　┃
 * ┃　　　┻　　　┃
 * ┃　　　　　　　┃
 * ┗━┓　　　┏━┛
 * 　　┃　　　┃神兽保佑
 * 　　┃　　　┃代码无BUG！
 * 　　┃　　　┗━━━┓
 * 　　┃　　　　　　　┣┓
 * 　　┃　　　　　　　┏┛
 * 　　┗┓┓┏━┳┓┏┛
 * 　　　┃┫┫　┃┫┫
 * 　　　┗┻┛　┗┻┛
 *
 * @Description: 代理商aop日志记录
 * @Author: Li.HuiNeng
 * @Create in: 2019-11-22 9:58
 * @Version: 1.0.1*/


//@Aspect
//@Component
public class AgentRecordAop {

    private static final Logger log = LoggerFactory.getLogger(AgentRecordAop.class);
    // 获取当前时间
    private String DATE = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());

    /*类型名称，用作比配记录*/
    private static final String AGENT_INFO_ENTITY = "AgentInfoEntity";
    private static final String AGENT_RELATE_ENTITY = "AgentRelateEntity";
    private static final String QR_CODE_AGENT_FEE = "QrCodeAgentFee";

    /** redis实例 */
    @Autowired
    private RedisTemplate redisTemplate;

    @Qualifier("taskExecutor")
    @Autowired
    private TaskExecutor taskExecutor;

    @Pointcut("execution(* com.zh.work.controller.platform.agent..*(..))" +
            " && ! execution(* com.zh.work.controller.platform.agent..updateAgentInfoById(..))" +
            " && ! execution(* com.zh.work.controller.platform.agent..putAgentAudit(..))" +
            " && @annotation(io.swagger.annotations.ApiOperation)" +
            " && !@annotation(org.springframework.web.bind.annotation.GetMapping)"
    )
    public void recordControllerLog() { }

    @Pointcut("execution(* com.zh.work.controller.platform.agent..updateAgentInfoById(..))")
    public void putControllerLog() { }


    @Async
    @AfterReturning(value = "recordControllerLog()", returning = "result")
    public void After(@NotNull JoinPoint jp, JSONResult result) {
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        String ip = IpAddressUtil.getIpAddress(request);
        // 获取当前操作人对象
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        Oauth auth = (Oauth) authentication.getPrincipal();
        // 排除为空参数之后保留的参数
        JSONObject json = new JSONObject();
        try {
            json = CheckObjAllFieldsIsNullUtil.getReflect(jp.getArgs()[0]);
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        AgentRecordEntity recordAop = new AgentRecordEntity();
        MethodSignature methodSignature = (MethodSignature) jp.getSignature();
        Method method = methodSignature.getMethod();
        try {
            //获取ApiOperation注解设置的notes描述内容作为信息
            if (method.isAnnotationPresent(ApiOperation.class)) {
                ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
                //方法完整路径,方法名,备注信息
                log.info("请求记录:{}.{}==>[ {} ],操作人=> [ {} ]", jp.getTarget().getClass().getName(), method.getName(), apiOperation.notes(), auth.getUserLoginName());
                recordAop.setRequestFunc(jp.getTarget().getClass().getName() + method.getName());
                recordAop.setCreateTime(DATE);
                recordAop.setIp(ip);
                recordAop.setOperatorId(auth.getId());
                recordAop.setRequestParam(json.toJSONString());
                recordAop.setSuccess(result.getSuccess());
                recordAop.setRemark(StringUtils.isNotEmpty(result.getMessage()) ? result.getMessage(): "");
                try {
                    recordAop.insert();
                } catch (Exception e) {
                    log.error("插入历史记录失败,错误原因:{}", e.getLocalizedMessage());
                }
            }
            // 将当前操作人id+当前时间(时分秒)作为key存入redis中,60天失效期
            redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, json, 60, TimeUnit.DAYS);
        } catch (Exception e) {
            log.error("异常信息:{}", e.getLocalizedMessage());
        }
    }

    @SuppressWarnings("all")
    @Async
    @Before(value = "putControllerLog()")
    public void beforeLog(@NotNull JoinPoint jp) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        AgentRecordEntity agentRecordEntity = new AgentRecordEntity();
        // 获取ip
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        String ip = IpAddressUtil.getIpAddress(request);
        // 获取方法信息
        MethodSignature methodSignature = (MethodSignature) jp.getSignature();
        Method method = methodSignature.getMethod();
        // 获取当前操作人对象
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        Oauth auth = (Oauth) authentication.getPrincipal();
        /** 获取ApiOperation注解设置的notes描述内容作为信息 */
        ApiOperation apiOperation = null;
        if (method.isAnnotationPresent(ApiOperation.class)) {
            apiOperation = method.getAnnotation(ApiOperation.class);
        }
        JSONObject json;
        Long id = null;
        if (jp.getArgs() == null) {
            log.warn("JoinPoint中传入目标方法的参数对象为空!");
        }
        int i = 0;
        if (!(jp.getArgs()[0] instanceof ArrayList)) {
            json = CheckObjAllFieldsIsNullUtil.getReflect(jp.getArgs()[0]);
            Assert.notNull(json.get("Id"),"修改前置增强,主键为空!");
            id = Long.parseLong(json.get("Id").toString());
            // 获取类名，并选择对应的类查询，添加记录
            String className = jp.getArgs()[0].getClass().getSimpleName();
            // 获取不同实体类修改前值,存入缓存
            try {
                if (id != null && !"".equals(id.toString())) {
                    switch (className) {
                        case AGENT_INFO_ENTITY:
                            AgentInfoEntity agentInfoEntity = new AgentInfoEntity();
                            AgentInfoEntity agentParams = JSON.toJavaObject(json, AgentInfoEntity.class);
                            agentInfoEntity = (AgentInfoEntity) agentInfoEntity.selectById(id);
                            // 如果修改的是代理商特定信息查询原值,插入记录变更表
                            if (!AgentContrasUtil.checkedProperty(agentInfoEntity,agentParams)) {
                                ChangeAgentAccount changeAgentAccount = AgentContrasUtil.setChangeAccountProperties(agentInfoEntity, agentParams);
                                changeAgentAccount.setUserName(auth.getUserLoginName());
                                if (changeAgentAccount.insert())
                                    log.info("代理商资料变更,记录修改流水记录!");
                            }
                            if (agentInfoEntity != null) {
                                i = 1;
                                this.redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, agentInfoEntity, 60, TimeUnit.DAYS);
                            }
                            assert agentInfoEntity != null;
                            agentRecordEntity.setBeforeValue(CheckObjAllFieldsIsNullUtil.getReflect(agentInfoEntity).toJSONString());
                            break;
                        case AGENT_RELATE_ENTITY:
                            AgentRelateEntity agentRelateEntity = new AgentRelateEntity();
                            agentRelateEntity = (AgentRelateEntity) agentRelateEntity.selectById(id);
                            if (agentRelateEntity != null) {
                                i = 1;
                                this.redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, agentRelateEntity, 60, TimeUnit.DAYS);
                            }
                            assert agentRelateEntity != null;
                            agentRecordEntity.setBeforeValue(CheckObjAllFieldsIsNullUtil.getReflect(agentRelateEntity).toJSONString());
                            break;
                        case QR_CODE_AGENT_FEE:
                            QrCodeAgentFee agentFee = new QrCodeAgentFee();
                            agentFee = (QrCodeAgentFee) agentFee.selectById(id);
                            if (agentFee != null) {
                                i = 1;
                                this.redisTemplate.opsForValue().set(auth.getUserLoginName() + DATE, agentFee, 60, TimeUnit.DAYS);
                            }
                            assert agentFee != null;
                            agentRecordEntity.setBeforeValue(CheckObjAllFieldsIsNullUtil.getReflect(agentFee).toJSONString());
                            break;
                        default:
                            log.warn("未找到对应数据类型,修改前数据未存入缓存,请尽快检查问题。");
                    }
                    agentRecordEntity.setAfterValue(json.toJSONString());
                    agentRecordEntity.setRequestParam(json.toJSONString());
                } else {
                    // 用于记录最终修改值
                    StringBuffer sb = new StringBuffer();
                    ArrayList arrayList = (ArrayList) jp.getArgs()[0];
                    arrayList.forEach(data -> {
                        sb.append(JSON.toJSONString(data) + "#");
                    });
                    sb.substring(0, sb.length() - 1);
                    agentRecordEntity.setRequestParam(sb.toString());
                }
                taskExecutor.execute(() -> {
                    agentRecordEntity.setRequestFunc(jp.getTarget().getClass().getName() + method.getName());
                    agentRecordEntity.setCreateTime(DATE);
                    agentRecordEntity.setIp(ip);
                    agentRecordEntity.setOperatorId(auth.getId());
                    agentRecordEntity.setSuccess(true);
                    try {
                        agentRecordEntity.insert();
                    } catch (Exception e) {
                        log.error("插入历史记录失败,错误原因:{}", e.getLocalizedMessage());
                    }
                });
                assert apiOperation != null;
                log.info("请求记录:{}.{}==>[ {} ]", jp.getTarget().getClass().getName(), method.getName(), apiOperation.notes());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }



    @Autowired
    private AgentRecordMapper agentRecordMapper;

    @Autowired
    private AgentInfoMapper agentInfoMapper;

    @Pointcut("execution(* com.zh.work.controller.platform.agent..putAgentAudit(..))")
    public void putAgentAuditLog() { }

    // 审核流水记录
    @Async
    @Before(value = "putAgentAuditLog()")
    public void beforeAuditLog(@NotNull JoinPoint jp) {
        // 获取当前操作人对象
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        Oauth auth = (Oauth) authentication.getPrincipal();
        String remark = "";
        try {
            Long id = Long.parseLong(jp.getArgs()[0].toString());
            int status = (int) jp.getArgs()[1];
            // 审核拒绝,添加描述信息(原因)
            if (status == 2) {
                remark = jp.getArgs()[2].toString();
            }
            // 通过id查询原值
            AgentInfoEntity agentInfoEntity = new AgentInfoEntity();
            AgentInfoEntity  origEntity = (AgentInfoEntity) agentInfoEntity.selectById(id);
            ChangeAgentAccount changeEntity = AgentContrasUtil.setChangeAccountProperties(origEntity, origEntity);
            changeEntity.setUserName(auth.getUserLoginName());
            changeEntity.setAgentNo(origEntity.getAgentNo());
            changeEntity.setAgentName(origEntity.getAgentName());
            changeEntity.setAuditStatus(status);
            // 上级代理商编号
//            changeEntity.setSuperAgentNo(auth.get());
            // 审核代理商编号
            String auditAgentNo = agentInfoMapper.getAgentInfoById(auth.getId().toString()).getAgentNo();
            changeEntity.setAuditAgent(auditAgentNo);
            if (!"".equals(remark)) changeEntity.setReason(remark);
            changeEntity.setAuditDate(new Date());
            if (agentRecordMapper.putAgentAuditHistory(changeEntity) > 0)
                log.info("插入一条审核记录,操作人  => {}", auth.getUserLoginName());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
